package com.xnfh.image

import android.graphics.*
import android.os.Build
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bumptech.glide.util.Preconditions
import com.bumptech.glide.util.Synthetic
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.Condition
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock

/**
 * @Description:
 * @Author: ziheng
 * @CreateDate: 2022/9/21 4:52 下午
 * @UpdateUser: ziheng
 * @UpdateDate: 2022/9/21 4:52 下午
 * @UpdateRemark: 更新说明
 */
object RoundTransformationUtils {
    // See #738.
    private val MODELS_REQUIRING_BITMAP_LOCK: Set<String> = HashSet(
        Arrays.asList( // Moto X gen 2
            "XT1085", "XT1092", "XT1093", "XT1094", "XT1095",
            "XT1096", "XT1097", "XT1098",  // Moto G gen 1
            "XT1031", "XT1028", "XT937C", "XT1032", "XT1008",
            "XT1033", "XT1035", "XT1034", "XT939G", "XT1039",
            "XT1040", "XT1042", "XT1045",  // Moto G gen 2
            "XT1063", "XT1064", "XT1068", "XT1069", "XT1072",
            "XT1077", "XT1078", "XT1079"
        )
    )

    /**
     * https://github.com/bumptech/glide/issues/738 On some devices, bitmap drawing is not thread
     * safe.
     * This lock only locks for these specific devices. For other types of devices the lock is always
     * available and therefore does not impact performance
     */
    private val BITMAP_DRAWABLE_LOCK =
        if (MODELS_REQUIRING_BITMAP_LOCK.contains(Build.MODEL)) ReentrantLock() else NoLock()

    private fun getAlphaSafeBitmap(
        pool: BitmapPool, maybeAlphaSafe: Bitmap
    ): Bitmap {
        val safeConfig = getAlphaSafeConfig(maybeAlphaSafe)
        if (safeConfig == maybeAlphaSafe.config) {
            return maybeAlphaSafe
        }
        val argbBitmap = pool[maybeAlphaSafe.width, maybeAlphaSafe.height, safeConfig]
        Canvas(argbBitmap).drawBitmap(maybeAlphaSafe, 0f /*left*/, 0f /*top*/, null /*paint*/)

        // We now own this Bitmap. It's our responsibility to replace it in the pool outside this method
        // when we're finished with it.
        return argbBitmap
    }

    private fun getAlphaSafeConfig(inBitmap: Bitmap): Bitmap.Config {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // Avoid short circuiting the sdk check.
            if (Bitmap.Config.RGBA_F16 == inBitmap.config) { // NOPMD
                return Bitmap.Config.RGBA_F16
            }
        }
        return Bitmap.Config.ARGB_8888
    }

    /**
     * Creates a bitmap from a source bitmap and rounds the corners.
     *
     *
     * This method does *NOT* resize the given [Bitmap], it only rounds it's corners.
     * To both resize and round the corners of an image, consider
     * [ ][inBitmap]
     */
    fun roundedCorners(
        pool: BitmapPool, inBitmap: Bitmap, leftTopRadius: Int, rightTopRadius: Int,
        leftBottomRadius: Int, rightBottomRadius: Int
    ): Bitmap {
        Preconditions.checkArgument(
            leftTopRadius >= 0,
            "leftTopRadius must be greater than 0."
        )
        Preconditions.checkArgument(
            rightTopRadius >= 0,
            "rightTopRadius must be greater than 0."
        )
        Preconditions.checkArgument(
            leftBottomRadius >= 0,
            "leftBottomRadius must be greater than 0."
        )
        Preconditions.checkArgument(
            rightBottomRadius >= 0,
            "rightBottomRadius must be greater than 0."
        )

        // Alpha is required for this transformation.
        val safeConfig = getAlphaSafeConfig(inBitmap)
        val toTransform = getAlphaSafeBitmap(pool, inBitmap)
        val result = pool[toTransform.width, toTransform.height, safeConfig]
        result.setHasAlpha(true)
        val shader = BitmapShader(
            toTransform, Shader.TileMode.CLAMP,
            Shader.TileMode.CLAMP
        )
        val paint = Paint()
        paint.isAntiAlias = true
        paint.shader = shader
        val rect = RectF(0f, 0f, result.width.toFloat(), result.height.toFloat())
        BITMAP_DRAWABLE_LOCK.lock()
        try {
            val canvas = Canvas(result)
            canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
            val mPath = Path()
            val mRadii = floatArrayOf(
                leftTopRadius.toFloat(), leftTopRadius.toFloat(),
                rightTopRadius.toFloat(), rightTopRadius.toFloat(),
                rightBottomRadius.toFloat(), rightBottomRadius.toFloat(),
                leftBottomRadius.toFloat(), leftBottomRadius
                    .toFloat()
            )
            mPath.addRoundRect(rect, mRadii, Path.Direction.CW)
            canvas.drawPath(mPath, paint)
            clear(canvas)
        } finally {
            BITMAP_DRAWABLE_LOCK.unlock()
        }
        if (toTransform != inBitmap) {
            pool.put(toTransform)
        }
        return result
    }

    // Avoids warnings in M+.
    private fun clear(canvas: Canvas) {
        canvas.setBitmap(null)
    }

    private class NoLock @Synthetic internal constructor() : Lock {
        override fun lock() {
            // do nothing
        }

        @Throws(InterruptedException::class)
        override fun lockInterruptibly() {
            // do nothing
        }

        override fun tryLock(): Boolean {
            return true
        }

        @Throws(InterruptedException::class)
        override fun tryLock(time: Long, unit: TimeUnit): Boolean {
            return true
        }

        override fun unlock() {
            // do nothing
        }

        override fun newCondition(): Condition {
            throw UnsupportedOperationException("Should not be called")
        }
    }
}